/**
 * RiskModel.java
 *
 * @author Michael Hansen
 * @author Lukas Thuernagel
 * @author Soeren Warszus
 */

package risk.game;

import java.util.*;
import javax.swing.JLabel; //Loeschen
import javax.swing.JOptionPane;
import javax.swing.JPanel; //Loeschen
import risk.player.*;
import risk.card.*;


public class RiskModel 
{
    /**
     * Legt WorldMap an
     */
    private WorldMap wm;
    
    /**
     * Legt die Spieler an.
     */
    private ArrayList<PlayerModel> alPlayer;
    
    /**
     * Zum speichern von Views
     */
    private Board boardView;
    
    /**
     * Zaehhler fuer gespielte Runden zum ermitteln des aktiven Spielers
     */
    private int PlayerCounter = 0;
    
    /**
     * Instanzvariable zur Repraesentation der Spielphase
     * state = 0  // Verteilung der Starteinheiten (Initialwert der PlayerModel´s)
     * state = 1  // Verteilung der Einheiten
     * state = 2  // Kampfphase mit anschliessendem Karteziehen
     * state = 3  // Truppenverschiebung
     */
    private int state = 0;
    
    /**
     * Instanzvariable mit dem dem RiskModel das Kartendeck uebergeben wird.
     */
    private Deck deck;
    
    /**
     * Temporaere Variable zur Ueberpruefung, ob neue Laender erobert wurden
     */
    private int newTerritoryCount;
    
    /**
     * Auflistung der an einem Kampf beteiligten Laender
     */
    private ArrayList<Territory> battleParty;
    
    /**
     * Auflistung der Laender, fuer die Truppenbewegung
     */
    private ArrayList<Territory> moveMilitaries;
    
    /**
     * Model fuer den Kampf
     */
    private CombatModel combatModel;
    /**
     * Ansicht fuer die Kampf
     */
    private CombatView combatView;

    /**
     * Model fuer die Truppenbewegung
     */
    private MoveMilitaryModel moveMilitaryModel;
    
    /**
     * Ansicht fuer die Truppenbewegung
     */
    private MoveMilitaryView moveMilitaryView;
    
    /**
     * Temporäre anzeige
     */
    private MoveMilitaryViewTemp moveMilitaryTemp;

    
    
    /**
     * Der Konstruktur erstellt eine neue Arraylist.
     * Diese wird effektiv nur ben�tigt wenn dem Model mehrere Views
     * zugeordnet werden.
     */
    public RiskModel (WorldMap wm, ArrayList<PlayerModel> alPlayer, Deck deck) 
    {
        this.wm = wm;
        this.alPlayer = alPlayer;
        this.deck = deck;
        for (int i=0; i<this.alPlayer.size();i++){
            //alPlayer.get(i).setUnitCount((2*this.wm.getTerritories().size())/alPlayer.size());
            alPlayer.get(i).setUnitCount(3);
        }
        
        
        battleParty = new ArrayList<Territory>();
        moveMilitaries = new ArrayList<Territory>();
        System.out.println("RiskModel: nextState: Kartenstapelgroesse: "+this.deck.getLength());
    }

    /**
     * Gibt eine Liste aller Spieler zurueck.
     */
    public ArrayList<PlayerModel> getPlayer ()
    {
        return alPlayer;
    }
    
    
    /**
     * Die Methode registriert einen View beim Model. 
     *
     * @param view Das zu registrierende Board.
     */
    public void registerView (Board view) 
    {
        if (view == null) {
            throw new IllegalArgumentException("Cannot add null object to RiskModel");
        }
        
        boardView = view;
        initializeView();
    }
    
    
    /**
     * Initialisierung der zusaetzlichen Views auf dem Board
     */
    private void initializeView(){
         //Kampfpanel erstellen und anzeigen
        this.combatModel = new CombatModel();
        this.combatView = new CombatView(this.combatModel,this.boardView.getController());
        
        this.moveMilitaryModel = new MoveMilitaryModel();
        this.moveMilitaryView = new MoveMilitaryView(this.moveMilitaryModel,this.boardView.getController());
        moveMilitaryTemp = new MoveMilitaryViewTemp(this.boardView.getController());
    }
    
    /**
     * Ermoeglicht das unregistrieren eines Views.
     *
     * @param view Das zu "loeschende" Board.
     */
    public void unregisterView()
    {
        boardView = null;
    }
    
    /**
     * Rueckgabe des CombatModel
     *
     * @return CombatModel combatModel. Model fuer den Kampf
     */
    public CombatModel getCombatModel(){
        return this.combatModel;
    }   
    
    /**
     * Zuruecksetzen des CombatModels
     */
    public void resetCombatModel(){
        this.combatModel.resetView();
    }
    
    /**
     * Methode zum Ermitteln des aktuellen Spielers
     *
     * @return PlayerModel pm Gibt den aktiven Spieler zur�ck.
     */
    public PlayerModel getActivePlayer(){
        PlayerModel pm = alPlayer.get(PlayerCounter % alPlayer.size());
        pm.getBoardView().setActive(true);
        pm.getBoardView().setState(pm.getStateString());
        if (PlayerCounter >= 1)
            alPlayer.get((PlayerCounter-1) % alPlayer.size()).getBoardView().setActive(false);
        return pm;
    }
    
    /**
     * Setzt den aktiven Spieler einen weiter.
     */
    public void nextPlayer(){
        PlayerModel pm = alPlayer.get(PlayerCounter % alPlayer.size());
        pm.getBoardView().setActive(false);
        alPlayer.get((PlayerCounter+1) % alPlayer.size()).getBoardView().setActive(true);
        this.PlayerCounter++;
    }
    
    /**
     * Methode zum einsehen der Karte
     *
     * @return WorldMap wm Gibt die aktuelle Karte zurueck.
     */
    public WorldMap getWorldMap(){
        return this.wm;
    }
    
    /**
     * Gibt die aktuelle Spielphase zurueck.
     *
     * @return int state aktuelle Spielphase.
     */
    public int getState(){
        return this.state;
    }
    
    /**
     * Geht in die naechste Spielphase ueber und ruft die Spielphasen
     * entsprechende Methode auf
     *
     * @param int i  Die anktuell angeforderte Phase
     */
    public void doState(int i){
        int militaries;
        //int tmpstate = getState();
        //this.state = (tmpstate % 3)+1;
        this.state = i;
        switch(this.state){
            case 1:{
                System.out.println("RiskModel: nextState: Phase: "+state);
                newTerritoryCount = this.getActivePlayer().getTerritoryList().size();
                System.out.println("RiskModel: nextState: Anzahl Länder: TerritoryCount "+newTerritoryCount);
                System.out.println("RiskModel: nextState: Anzahl Länder: Spieler "+this.getActivePlayer().getTerritoryList().size());
                this.getActivePlayer().getCardView().initializeView();
                this.boardView.switchPanel(this.getActivePlayer().getCardView());
                this.buildUpMilitaries();
                this.getActivePlayer().setState(state);
                break;
            }
            case 2:{
                System.out.println("RiskModel: nextState: Phase: "+state);
                resetCombatModel();
                if(this.getActivePlayer().getCardModel().getTmpCardList().size() > 0){
                    for(int j=0;j<this.getActivePlayer().getCardModel().getTmpCardList().size();j++){
                        this.deck.insert(this.getActivePlayer().getCardModel().getTmpCardList().get(j));
                    }
                    this.getActivePlayer().getCardModel().deleteTmpCardList();
                    this.deck.shuffle();
                }
                this.boardView.switchPanel(this.combatView);
                this.getActivePlayer().setState(state);
                break;
            }
            case 3:{
                System.out.println("RiskModel: nextState: Phase: "+state);
                System.out.println("RiskModel: nextState: Aktiver Spielername "+this.getActivePlayer().getPlayerName());
                System.out.println("PlayerModel: setTerritory: HashCode: "+this.getActivePlayer().hashCode());
                System.out.println("RiskModel: nextState: Anzahl Länder: TerritoryCount "+newTerritoryCount);
                System.out.println("RiskModel: nextState: Anzahl Länder: Spieler "+this.getActivePlayer().getTerritoryList().size());
                if(newTerritoryCount < this.getActivePlayer().getTerritoryList().size()){
                    this.getActivePlayer().setCard(this.deck.takeFirst());
                }
                this.boardView.getMapView().removeBattleTerritories();
                this.beginOfMoveTrupps();
                this.getActivePlayer().setState(state);
                break;
            }
        }    
    }
    
    /**
     * Truppen zaehlen, die der Spieler verteilen darf
     *
     */
    private void buildUpMilitaries(){
        PlayerModel pm = this.getActivePlayer();
        int continentBonus=0;
        int territoryBonus = 3;
        //TerritoyBonus
        if(territoryBonus<(pm.getTerritoryCount()/3)){
            territoryBonus = pm.getTerritoryCount()/3;
        }
        //KontinentBonus
        for(int i=0; i<pm.getContinentCount();i++){
            continentBonus += pm.getContinentList().get(i).getPlayerBonus(pm);
        }
        pm.setUnitCount(territoryBonus+continentBonus);
    }
        
    /**
     * Sammeln der am Kampf beteiligten Laender und Weitergabe an die Combat-Klasse
     *
     * @param Territory territory das am Kampf beteiligte Land
     */
    public void battleState(Territory territory){
        if (this.battleParty.size() >= 2) { 
            this.battleParty.clear();
            boardView.getMapView().removeBattleTerritories();
        }
        //Erstes Land muss zum Angreifer gehoeren
        if (this.battleParty.size()==0 && this.getActivePlayer()==territory.getOwner() && territory.getArmySize() > 1){
            if(boardView.getMapView().isPaintingFrontiers()){
                boardView.getMapView().removeBattleTerritories();
            }
            boardView.getMapView().paintFrontiers(territory);
            battleParty.add(territory);
        }
        else if (this.battleParty.size()==0 && this.getActivePlayer()!=territory.getOwner() && territory.getArmySize() > 1){
            System.out.println("Bitte eigenes Land mit einer Armeeanzahl von mindestens 2 waehlen!!");
        }//Zweites Land ist das anzugreifende Land
        else if (this.battleParty.size()==1){ 
            if (this.battleParty.get(0)==territory){
                boardView.getMapView().removeBattleTerritories();
                this.battleParty.clear();
            }
            else{
                ArrayList neighboursTerritory = battleParty.get(0).getNeighbors();
                //Pruefen, ob das Land ein Nachbarland des anzugreifenden Landes ist
                for (int i=0; i<neighboursTerritory.size();i++){
                    String neighbour = (String) neighboursTerritory.get(i);
                    if (territory.getOwner()!=this.getActivePlayer() && neighbour.compareTo(territory.getName())==0){
                        boardView.getMapView().paintFrontiers(territory);
                        battleParty.add(territory);
                        i = neighboursTerritory.size();
                    }
                }//rof
                if (this.battleParty.size()==2){
                    //Combat aufrufen und die Territories uerbergeben
                    System.out.println("RiskModel: battleState: Land1: "+this.battleParty.get(0).getName()+" Land2: "+this.battleParty.get(1).getName());
                    resetCombatModel();
                    this.combatModel.setCombatTerritories(this.battleParty.get(0), this.battleParty.get(1));
                    //this.boardView.switchPanel(this.combatView);
                    //Laendergrenzen, der am Kampf beteiligten laender wieder loeschen
                    this.battleParty.clear();
                    //boardView.getMapView().removeBattleTerritories();
                }
                else{        
                    //MessageBox
                    System.out.println("Bitte waehlen Sie ein angrenzendes gegerisches Land aus!!");
                }
            }
        }
    }    
    
    /**
     * Setzen der Kontinentbesitzer
     */
    public void checkContinentBonus(){
        PlayerModel tmp = null;
        boolean bool = false;
        ArrayList<Continent> alC = this.wm.getContinents();
        for(int i=0; i<alC.size(); i++){
            if(alC.get(i).getOwner()!=null){
                tmp=alC.get(i).getOwner();
            }
            //Fuer jeden Kontinent den Owner pruefen
            alC.get(i).setPossibleOwner();
            //Ist der aktuelle Spieler auch der Kontinentbesitzer, dann hinzufuegen zu seiner Kontinentliste
            if(alC.get(i).getOwner()==this.getActivePlayer()){
                for(int j=0;j<this.getActivePlayer().getContinentList().size();j++){
                    if(this.getActivePlayer().getContinentList().get(j)==alC.get(i)){
                        bool = true;
                    }
                }
                if(!bool){
                    if(tmp!=null){
                        if(tmp!=this.getActivePlayer()){
                            tmp.deleteContinent(alC.get(i));
                        }
                    }
                    this.getActivePlayer().setContinent(alC.get(i));
                }
                
                System.out.println("RiskModel: checkContinentBonus: Kontinent: "+alC.get(i).getName());
                //System.out.println("RiskModel: checkContinentBonus: Old Owner: "+tmp.getPlayerName());
                System.out.println("RiskModel: checkContinentBonus: New Owner: "+alC.get(i).getOwner().getPlayerName());
            }
            if(tmp!=null){
                if(tmp!=alC.get(i).getOwner()){
                    tmp.deleteContinent(alC.get(i));
                }
            }
        }
        for(int j=0; j<this.alPlayer.size();j++){
            alPlayer.get(j).getBoardView().updateView();
        }
    }
    
    /**
     * Pruefen, ob das Spiel beendet ist
     */
    public void checkEndOfGame(){
        if(this.wm.getTerritories().size()==this.getActivePlayer().getTerritoryList().size()){
            System.out.println("The winner is ........ "+this.getActivePlayer().getPlayerName());
            JPanel winner = new JPanel();
            winner.add(new JLabel("The Winner is "+this.getActivePlayer().getPlayerName()));
            winner.setVisible(true);
            this.boardView.switchPanel(winner);
        }
    }

    /**
     * Truppenbewegung (Phase 3) von einem Land in ein weiteres Land. Beide Laender muessen zum
     * Aktiven Spieler gehoeren. Erst werden die Laender gesammelt, sobald 2 aneinandergrenzende
     * Laender ermittelt wurden, koennen Armeen verschoben werden
     *
     * @param Territory territory Land, das fuer die Truppenbewegung genutzt wird 
     */
    public void moveTrupps(Territory territory){
        //Pruefen, ob der Spieler Besetzer des Landes ist
        if(this.getActivePlayer()!=territory.getOwner()){
           System.out.println("Bitte eigenes land waehlen!!");
        }//Erstes land ist das Land aus dem Armeen gezogen werden
        else if(this.moveMilitaries.size()==0 && this.getActivePlayer()==territory.getOwner()){
            boardView.getMapView().paintFrontiers(territory);
            moveMilitaries.add(territory);
        }//Zweites Land, in das Truppen verschoben werden sollen
        else if(this.moveMilitaries.size()==1 && this.getActivePlayer()==territory.getOwner()){
            if(this.moveMilitaries.get(0)==territory){
                boardView.getMapView().removeBattleTerritories();
                this.moveMilitaries.clear();
                boardView.getMapView().removeBattleTerritories();
            }
            else{
                ArrayList neighboursTerritory = moveMilitaries.get(0).getNeighbors();
                //Pruefen, ob das Land ein Nachbarland des anzugreifenden Landes ist
                for(int i=0; i<neighboursTerritory.size();i++){
                    String neighbour = (String) neighboursTerritory.get(i);
                    if(territory.getOwner()==this.getActivePlayer() && neighbour.compareTo(territory.getName())==0){
                        boardView.getMapView().paintFrontiers(territory);
                        moveMilitaries.add(territory);
                        i = neighboursTerritory.size();
                    }
                 }//rof
            }
            if(this.moveMilitaries.size()==2){
                //MoveMilitary aufrufen und die Territories uerbergeben
                this.moveMilitaryModel.setTerritories(moveMilitaries.get(0),moveMilitaries.get(1));
                this.boardView.switchPanel(this.moveMilitaryView);
            }
            else{
                //MessageBox
                System.out.println("Bitte waehlen Sie ein Ihnen gehoerendes angrenzendes Land aus!!");
            }
        }
    }
    
    /**
     * Beginn der Phase der Truppenbewegung
     */
    public void beginOfMoveTrupps(){
        this.boardView.switchPanel(moveMilitaryTemp);
    }
    
    /**
     * Beenden des View fuer die Truppenbewegung
     *
     * @param Boolean bool, ob Truppen bewegt wurden
     */
    public void endOfMovedTrupps(Boolean bool){
        if(bool){
            if(this.moveMilitaryModel.getMovedTrupps()>0){
                 ArrayList<Territory> alT = this.wm.getTerritories();
                 for(int i=0;i<alT.size();i++){
                     if(alT.get(i)==moveMilitaries.get(0)){
                        alT.get(i).setArmySize(moveMilitaries.get(0).getArmySize()-this.moveMilitaryModel.getMovedTrupps());
                    }
                    else if(alT.get(i)==moveMilitaries.get(1)){
                        alT.get(i).setArmySize(moveMilitaries.get(1).getArmySize()+this.moveMilitaryModel.getMovedTrupps());
                    }
                 }
            //Laendergrenzen, der Laender, die an den Truppenbewegungen teilgenommen haben, wieder loeschen
            boardView.getMapView().removeBattleTerritories();
            moveMilitaries.clear();
            //Ansicht fuer die Truppenbewegung neuaufbauen
            this.moveMilitaryModel.resetView();
            nextPlayer();
            doState(1);
            }
            else{
                boardView.getMapView().removeBattleTerritories();
                moveMilitaries.clear();
                //Ansicht fuer die Truppenbewegung neuaufbauen
                this.moveMilitaryModel.resetView();
                doState(3);
            }
        }
        else{
            //Laendergrenzen, der Laender, die an den Truppenbewegungen teilgenommen haben, wieder loeschen
            boardView.getMapView().removeBattleTerritories();
            boardView.getMapView().repaint();
            moveMilitaries.clear();
            //Ansicht fuer die Truppenbewegung neuaufbauen
            this.moveMilitaryModel.resetView();
        }

    }
}
